
;; via Ladicle's Emacs Configuration
;; ladicle.com/post/config/
(defun p1uxtar-kill-word-at-point ()
  "Smart kill word or region."
  (interactive)
  (let ((char (char-to-string (char-after (point)))))
    (cond
     ((string= " " char)
      (delete-horizontal-space))
     ((string-match "[\t\n -@\[-`{-~],.、。" char)
      (kill-word 1))
     (t
      (forward-char)
      (backward-word)
      (kill-word 1)))))
(global-set-key (kbd "M-d")  'p1uxtar-kill-word-at-point)

(defun p1uxtar-backward-kill-word-or-region (&optional arg)
  (interactive "p")
  (if (region-active-p)
      (call-interactively #'kill-region)
    (backward-kill-word arg)))
;; (global-set-key (kbd "C-w")  'p1uxtar-backward-kill-word-or-region)
;; replace with LazyCat/thing-edit

(defun p1uxtar-select-text-in-quote ()
  "Select text between the nearest left and right delimiters.
Delimiters here includes the following chars: '\"`<>(){}[]“”‘’‹›«»「」『』【】〖〗《》〈〉〔〕（）
This command select between any bracket chars, not the inner text of a bracket. For example, if text is
 (a(b)c▮)
 the selected char is “c”, not “a(b)c”.
URL `http://ergoemacs.org/emacs/modernization_mark-word.html'
Version 2020-03-11"
  (interactive)
  (let (
        ($skipChars "^'\"`<>(){}[]“”‘’‹›«»「」『』【】〖〗《》〈〉〔〕（）〘〙")
        $p1
        )
    (skip-chars-backward $skipChars)
    (setq $p1 (point))
    (skip-chars-forward $skipChars)
    (set-mark $p1)))

(defun p1uxtar-select-block ()
  "Select the current/next block of text between blank lines.
If region is active, extend selection downward by block.
URL `http://ergoemacs.org/emacs/modernization_mark-word.html'
Version 2019-12-26"
  (interactive)
  (if (region-active-p)
      (re-search-forward "\n[ \t]*\n" nil "move")
    (progn
      (skip-chars-forward " \n\t")
      (when (re-search-backward "\n[ \t]*\n" nil "move")
        (re-search-forward "\n[ \t]*\n"))
      (push-mark (point) t t)
      (re-search-forward "\n[ \t]*\n" nil "move"))))
(global-set-key (kbd "M-h") 'p1uxtar-select-block)

;; delete empty lines
;; @mayingjun via emacs-cn
(defun p1uxtar-delete-empty-line (beg end)
  "Delete empty lines if selected, otherwise the whole buffer."
  (interactive "r")
  (let ((empty-line "^[\t  ]*$"))
    (save-excursion
      (if (region-active-p)
          (flush-lines empty-line beg end)
        (flush-lines empty-line (point-min) (point-max))))))

;; Delete Enclosed Text by Xah
;; ergoemacs.org/emacs/elisp_examples.html
(defun p1uxtar-delete-enclosed-text ()
  "Delete texts between any pair of delimiters."
  (interactive)
  (save-excursion
    (let (p1 p2)
      (skip-chars-backward "^([<>“\"")
      (setq p1 (point))
      (skip-chars-forward "^)]<>”\"")
      (setq p2 (point))
      (delete-region p1 p2))))
(global-set-key (kbd "C-S-k") 'p1uxtar-delete-enclosed-text)

;; case sensitive replace
(defadvice replace-string (around turn-off-case-fold-search)
  (let ((case-fold-search nil))
    ad-do-it))
(ad-activate 'replace-string)

;; open file or dired at cursor
;; via: emacs-china.org/t/topic/14580/17 by Xah
(defun p1uxtar-open-file-at-cursor ()
  "Open the file path under cursor.
If there is text selection, uses the text selection for path.
If the path starts with “http://”, open the URL in browser.
Input path can be {relative, full path, URL}.
Path may have a trailing “:‹n›” that indicates line number. If so, jump to that line number.
If path does not have a file extension, automatically try with “.el” for elisp files.
This command is similar to `find-file-at-point' but without prompting for confirmation.
URL `http://ergoemacs.org/emacs/emacs_open_file_path_fast.html'
Version 2018-10-08"
  (interactive)
  (let* (($inputStr (if (use-region-p)
                        (buffer-substring-no-properties (region-beginning) (region-end))
                      (let ($p0 $p1 $p2
                                ;; chars that are likely to be delimiters of file path or url, e.g. space, tabs, brakets. The colon is a problem. cuz it's in url, but not in file name. Don't want to use just space as delimiter because path or url are often in brackets or quotes as in markdown or html
                                ($pathStops "^  \t\n\"`'‘’“”|()[]{}「」<>〔〕〈〉《》【】〖〗«»‹›❮❯❬❭〘〙·。\\"))
                        (setq $p0 (point))
                        (skip-chars-backward $pathStops)
                        (setq $p1 (point))
                        (goto-char $p0)
                        (skip-chars-forward $pathStops)
                        (setq $p2 (point))
                        (goto-char $p0)
                        (buffer-substring-no-properties $p1 $p2))))
         ($path
          (replace-regexp-in-string
           "^file:///" "/"
           (replace-regexp-in-string
            ":\\'" "" $inputStr))))
    (if (string-match-p "\\`https?://" $path)
        (if (fboundp 'xahsite-url-to-filepath)
            (let (($x (xahsite-url-to-filepath $path)))
              (if (string-match "^http" $x )
                  (browse-url $x)
                (find-file $x)))
          (progn (browse-url $path)))
      (if ; not starting “http://”
          (string-match "^\\`\\(.+?\\):\\([0-9]+\\)\\'" $path)
          (let (
                ($fpath (match-string 1 $path))
                ($line-num (string-to-number (match-string 2 $path))))
            (if (file-exists-p $fpath)
                (progn
                  (find-file $fpath)
                  (goto-char 1)
                  (forward-line (1- $line-num)))
              (when (y-or-n-p (format "file no exist: 「%s」. Create?" $fpath))
                (find-file $fpath))))
        (if (file-exists-p $path)
            (progn ; open f.ts instead of f.js
              (let (($ext (file-name-extension $path))
                    ($fnamecore (file-name-sans-extension $path)))
                (if (and (string-equal $ext "js")
                         (file-exists-p (concat $fnamecore ".ts")))
                    (find-file (concat $fnamecore ".ts"))
                  (find-file $path))))
          (if (file-exists-p (concat $path ".el"))
              (find-file (concat $path ".el"))
            (when (y-or-n-p (format "file no exist: 「%s」. Create?" $path))
              (find-file $path ))))))))
(global-set-key (kbd "C-x f") 'p1uxtar-open-file-at-cursor)

;; -------- refresh current buffer --------
(defun revert-current-buffer ()
  "Revert the current buffer."
  (interactive)
  (message "Revert this buffer")
  (text-scale-set 0)
  (widen)
  (revert-buffer t t))
(global-set-key (kbd "<f5>") #'revert-current-buffer)

;; -------- open init config file --------
(defun p1uxtar-open-init-file ()
  "Open to edit init.el file."
  (interactive)
  (find-file "~/.emacs.d/init.el"))
(global-set-key (kbd "C-c i") #'p1uxtar-open-init-file)

(defun p1uxtar-repeat ()
  "Repeat last command."
  (interactive)
  (eval (car command-history)))

;; -------- split window right/below --------
;; via emacs-china.org/t/topic/945/2
(defun p1uxtar-split-window-right ()
  "Split window with another buffer."
  (interactive)
  (select-window (split-window-right))
  (switch-to-buffer (other-buffer)))
(global-set-key (kbd "C-x 3") #'p1uxtar-split-window-right)

(defun p1uxtar-split-window-below ()
  "Split window with another buffer."
  (interactive)
  (select-window (split-window-below))
  (switch-to-buffer (other-buffer)))
(global-set-key (kbd "C-x 2") #'p1uxtar-split-window-below)

;; ;; before use package `rotate'
;; ;; switch current windows layout
;; (defun p1uxtar-switch-windows-layouts ()
;;   "Switch windows to vertical or horizontal."
;;   (interactive)
;;   (when (eq (length (window-list)) 2)
;;     (cond
;;      ((eq (window-height) (frame-height))
;;       (delete-window)
;;       (p1uxtar-split-window-below))
;;      ((or (eq (window-width) (frame-width))
;; 	  (eq (window-width) (- (frame-width) 1)))
;;       (delete-window)
;;       (p1uxtar-split-window-right)))))

;; -------- where the current file is --------
;; via: octolinker-demo.now.sh/MatthewZMD/.emacs.d#company-tabnine
(defun p1uxtar-get-current-directory ()
  "An interactive function that displays `buffer-file-name' when visiting a file.
Otherwise the function displays `buffer-name'."
  (interactive)
  (let ((dir-file (buffer-file-name)))
    (if dir-file
        (message dir-file)
      (message (buffer-name)))))

(defun p1uxtar-last-buffer (arg)
  "Switch to last buffer.
Argument ARG if not nil, switching in a new window."
  (interactive "P")
  (cond
   ((minibufferp)
    (keyboard-escape-quit))
   ((not arg)
    (mode-line-other-buffer))
   (t
    (split-window)
    (mode-line-other-buffer))))
(global-set-key (kbd "<escape>") #'p1uxtar-last-buffer)

;; -------- testing speed of elpa mirrors --------
;; via: emacs-china.org/t/elpa/11192/7 by xuchunyang
(require 'chart)
(defun p1uxtar-fastest-elpa-mirror ()
  (interactive)
  (let* ((urls
          (mapcar
           (lambda (part)
             (concat "http://" part "archive-contents"))
           '("melpa.org/packages/"
             "www.mirrorservice.org/sites/melpa.org/packages/"
             "emacs-china.org/melpa/"
             "mirrors.tuna.tsinghua.edu.cn/elpa/melpa/"
             "mirrors.163.com/elpa/melpa/"
             "mirrors.cloud.tencent.com/elpa/melpa/")))
         (durations
          (mapcar
           (lambda (url)
             (let ((start (current-time)))
               (message "Fetching %s" url)
               (call-process "curl" nil nil nil "--max-time" "10" url)
               (float-time (time-subtract (current-time) start))))
           urls)))
    (chart-bar-quickie
     'horizontal
     "The fastest elpa mirror"
     (mapcar (lambda (url) (url-host (url-generic-parse-url url))) urls) "Elpa"
     (mapcar (lambda (d) (* 1e3 d)) durations) "ms")
    (message "%s" durations)))

;; -------- revert current buffer as sudo --------
(defun reopen-file-with-sudo ()
  (interactive)
  (find-alternate-file (format "/sudo::%s" (buffer-file-name))))

;; -------- processing before each quit --------
(defun p1uxtar-bye-emacs ()
  (interactive)
  ;; delete custom.el
  (let*
      ((p1-custom-el "~/.emacs.d/custom.el")
       (p1-rm-custom "rm ~/.emacs.d/custom.el"))
    (cond
     ((string-equal system-type "gnu/linux")
      (if (file-exists-p p1-custom-el)
          (shell-command p1-rm-custom)))
     ((eq system-type 'windows-nt)
      (if (file-exists-p p1-custom-el)
          (eshell-command p1-rm-custom)))))
  ;; set package-list to `package-selected-packages'
  (setq package-selected-packages p1uxtar-package-list)
  ;; remove packages Not in the list
  (package-autoremove)
  ;; C-x C-c
  (save-buffers-kill-terminal))
(global-set-key (kbd "C-x C-c") #'p1uxtar-bye-emacs)

(provide 'p1uxtar-funcs)
;;; p1uxtar-funcs ends here
